package com.xd.common.util;

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.symmetric.AES;
import com.xd.common.exception.Assert;
import com.xd.common.exception.XkdException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
 * AES加密工具类
 *
 * @author ACGkaka
 * @since 2021-06-18 19:11:03
 */
public class AESUtil {
    /**
     * 日志相关
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(AESUtil.class);

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 编码
     */
    private static final String ENCODING = "UTF-8";
    /**
     * 算法定义
     */
    private static final String AES_ALGORITHM = "AES";
    /**
     * 指定填充方式
     */
    private static final String AES_ECB_PKCS_7_PADDING = "AES/ECB/PKCS7Padding";
    private static final String AES_ECB_PKCS_5_PADDING = "AES/ECB/PKCS5Padding";
    private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS5Padding";
    /**
     * 偏移量(CBC中使用，增强加密算法强度)
     */
    private static final String IV_SEED = "1234567812345678";

    /**
     * AES加密
     *
     * @param content 待加密内容
     * @param aesKey  密码
     * @return
     */
    public static String encrypt(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            LOGGER.info("AES encrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置加密算法，生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
                //选择加密
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
                //根据待加密内容生成字节数组
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                //返回base64字符串
                return java.util.Base64.getEncoder().encodeToString(encrypted);
                // return Base64Utils.encodeToString(encrypted);
            } catch (Exception e) {
                LOGGER.info("AES encrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }

        } else {
            LOGGER.info("AES加密密钥长度不为16位:" + aesKey);
            throw new XkdException("密钥长度不为16位");
//            LOGGER.info("AES encrypt: the aesKey is null or error!");
//            return null;
        }
    }

    //    public static void main(String[] args) throws IOException {
//        String decrypt = AESUtil.decrypt("fBV/cdT/2RmROSLdOdL01ZYvkQomGcdBtEGz3bN9RynQNpaZlkndcLrN6gjhNlYcoX4Fktb70mozyn3bkc3CMZ4MbNEQ1LqKVcTEf5PzlvtU/bMIqqdJXeVKOIT1iTwxFPVh0dODsIMRybtODJyAbHDTT4jgOX3zweR4f2cnNyfmtUle4xfx2NXp4Lx4k8yvzdpMLSA3lYt9H/RK4A0A4/G5YBlLGQF1z/M51Rc0cwrg9zFZsXr/wiLu30fubvQm5diPTVFxtP3AgSi4+kMhyIzBMDAX40ptCAwrLQqlrH/wQon1WvkBwaV+MvSNYM+Y1rYBBTmORP5MhyHh/bhLVXGNmMiITO4/legO9K+r2io=", "axW9zT4BD9siImpC");
//        new BASE64Encoder().encode(bytes)
//        System.out.println(new BASE64Encoder().encode("fBV/cdT/2RmROSLdOdL01ZYvkQomGcdBtEGz3bN9RynQNpaZlkndcLrN6gjhNlYcoX4Fktb70mozyn3bkc3CMZ4MbNEQ1LqKVcTEf5PzlvtU/bMIqqdJXeVKOIT1iTwxFPVh0dODsIMRybtODJyAbHDTT4jgOX3zweR4f2cnNyfmtUle4xfx2NXp4Lx4k8yvzdpMLSA3lYt9H/RK4A0A4/G5YBlLGQF1z/M51Rc0cwrg9zFZsXr/wiLu30fubvQm5diPTVFxtP3AgSi4+kMhyIzBMDAX40ptCAwrLQqlrH/wQon1WvkBwaV+MvSNYM+Y1rYBBTmORP5MhyHh/bhLVXGNmMiITO4/legO9K+r2io=".getBytes()));
//
//        System.out.println(new BASE64Decoder().decodeBuffer("ZkJWL2NkVC8yUm1ST1NMZE9kTDAxWll2a1FvbUdjZEJ0RUd6M2JOOVJ5blFOcGFabGtuZGNMck42Z2poTmxZY29YNEZrdGI3MG1veg0KeW4zYmtjM0NNWjRNYk5FUTFMcUtWY1RFZjVQemx2dFUvYk1J cXFkSlhlVktPSVQxaVR3eEZQVmgwZE9Ec0lNUnlidE9ESnlBYkhEVA0KVDRqZ09YM3p3ZVI0ZjJj bk55Zm10VWxlNHhmeDJOWHA0THg0azh5dnpkcE1MU0EzbFl0OUgvUks0QTBBNC9HNVlCbExHUUYx ei9NNQ0KMVJjMGN3cmc5ekZac1hyL3dpTHUzMGZ1YnZRbTVkaVBUVkZ4dFAzQWdTaTQra01oeUl6 Qk1EQVg0MHB0Q0F3ckxRcWxySC93UW9uMQ0KV3ZrQndhVitNdlNOWU0rWTFyWUJCVG1PUlA1TWh5 SGgvYmhMVlhHTm1NaUlUTzQvbGVnTzlLK3IyaW89"));
//    }
    public static void main(String[] args) {
//        String decrypt = AESUtil.decrypt("fBV/cdT/2RmROSLdOdL01ZYvkQomGcdBtEGz3bN9RynQNpaZlkndcLrN6gjhNlYcoX4Fktb70mozyn3bkc3CMZ4MbNEQ1LqKVcTEf5PzlvtU/bMIqqdJXeVKOIT1iTwxFPVh0dODsIMRybtODJyAbHDTT4jgOX3zweR4f2cnNyfmtUle4xfx2NXp4Lx4k8yvzdpMLSA3lYt9H/RK4A0A4/G5YBlLGQF1z/M51Rc0cwrg9zFZsXr/wiLu30fubvQm5diPTVFxtP3AgSi4+kMhyIzBMDAX40ptCAwrLQqlrH/wQon1WvkBwaV+MvSNYM+Y1rYBBTmORP5MhyHh/bhLVXGNmMiITO4/legO9K+r2io=", "axW9zT4BD9siImpC");
//        System.out.println(decrypt);
//
//        String originalText = AESUtil.encrypt(decrypt, "axW9zT4BD9siImpC"); // 原始文本
//
//        System.out.println(originalText);
//
//        byte[] encodedBytes1 = java.util.Base64.getEncoder().encode(originalText.getBytes()); // 第一次加密
//        String encryptedText1 = new String(encodedBytes1);
//
//        System.out.println("First Encrypted Text: " + encryptedText1);
////"ZkJWL2NkVC8yUm1ST1NMZE9kTDAxWll2a1FvbUdjZEJ0RUd6M2JOOVJ5blFOcGFabGtuZGNMck42Z2poTmxZY29YNEZrdGI3MG1veg0KeW4zYmtjM0NNWjRNYk5FUTFMcUtWY1RFZjVQemx2dFUvYk1JcXFkSlhlVktPSVQxaVR3eEZQVmgwZE9Ec0lNUnlidE9ESnlBYkhEVA0KVDRqZ09YM3p3ZVI0ZjJjbk55Zm10VWxlNHhmeDJOWHA0THg0azh5dnpkcE1MU0EzbFl0OUgvUks0QTBBNC9HNVlCbExHUUYxei9NNQ0KMVJjMGN3cmc5ekZac1hyL3dpTHUzMGZ1YnZRbTVkaVBUVkZ4dFAzQWdTaTQra01oeUl6 Qk1EQVg0MHB0Q0F3ckxRcWxySC93UW9uMQ0KV3ZrQndhVitNdlNOWU0rWTFyWUJCVG1PUlA1TWh5SGgvYmhMVlhHTm1NaUlUTzQvbGVnTzlLK3IyaW89";
//        encryptedText1 = "ZkJWL2NkVC8yUm1ST1NMZE9kTDAxWll2a1FvbUdjZEJ0RUd6M2JOOVJ5blFOcGFabGtuZGNMck42Z2poTmxZY29YNEZrdGI3MG1venluM2JrYzNDTVo0TWJORVExTHFLVmNURWY1UHpsdnRVL2JNSXFxZEpYZVZLT0lUMWlUd3hGUFZoMGRPRHNJTVJ5YnRPREp5QWJIRFRUNGpnT1gzendlUjRmMmNuTnlmbXRVbGU0eGZ4Mk5YcDRMeDRrOHl2emRwTUxTQTNsWXQ5SC9SSzRBMEE0L0c1WUJsTEdRRjF6L001MVJjMGN3cmc5ekZac1hyL3dpTHUzMGZ1YnZRbTVkaVBUVkZ4dFAzQWdTaTQra01oeUl6Qk1EQVg0MHB0Q0F3ckxRcWxySC93UW9uMVd2a0J3YVYrTXZTTllNK1kxcllCQlRtT1JQNU1oeUhoL2JoTFZYR05tTWlJVE80L2xlZ085SytyMmlvPQ==";
//        byte[] decodedBytes2 = java.util.Base64.getDecoder().decode(encryptedText1.getBytes()); // 第二次解密
//        String decryptedText2 = new String(decodedBytes2);
//
//        System.out.println("Second Decrypted Text: " + decryptedText2);
//        String SECRET_KEY = "7faca8f987d3fe82";
//        String  Str = "{\"phone md5\":\"64b534b1893bdb387d2712fe5ce8fcf3\",\"age\":30}";
//        String encrypt = encrypt(Str, SECRET_KEY);
//        System.out.println(encrypt);
//        System.out.println(decrypt(encrypt, SECRET_KEY));
    }

    /**
     * 二进制模式AES_ECB_PKCS_5_PADDING
     *
     * @param content
     * @param aesKey
     * @return
     */
    public static String encryptAESBase64(String content, String aesKey) {
        return Base64Utils.encodeToString(encrypt(content, aesKey).getBytes());
    }

    /**
     * 二进制模式AES_ECB_PKCS_5_PADDING
     *
     * @param content
     * @param aesKey
     * @return
     */
    public static String encryptData(String content, String aesKey) {
        return new AES(HexUtil.decodeHex(aesKey)).encryptHex(content);
    }


    /**
     * 解密
     *
     * @param content 待解密内容
     * @param aesKey  密码
     * @return
     */
    public static String decrypt(String content, String aesKey) {
        if (StringUtils.isBlank(content)) {
            LOGGER.info("AES decrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置解密算法，生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_5_PADDING);
                //选择解密
                cipher.init(Cipher.DECRYPT_MODE, skeySpec);

                //先进行Base64解码
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);

                //根据待解密内容进行解密
                byte[] decrypted = cipher.doFinal(decodeBase64);
                //将字节数组转成字符串
                return new String(decrypted, ENCODING);
            } catch (Exception e) {
                LOGGER.info("AES decrypt exception:" + e.getMessage());
                return null;
            }
        } else {
            LOGGER.info("AES解密密钥长度不为16位:" + aesKey);
            throw new XkdException("密钥长度不为16位");
        }
    }

    /**
     * AES_CBC加密
     *
     * @param content 待加密内容
     * @param aesKey  密码
     * @param iv_seed 偏移量
     * @return
     */
    public static String encryptCBC(String content, String aesKey, String iv_seed) {
        Assert.no(content, "加密数据为空");
//        if (StringUtils.isBlank(content)) {
//            LOGGER.info("AES_CBC encrypt: the content is null!");
//            return null;
//        }
        //判断秘钥是否为16位
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置加密算法，生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                //偏移
                IvParameterSpec iv = new IvParameterSpec(iv_seed.getBytes(ENCODING));
                //选择加密
                cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
                //根据待加密内容生成字节数组
                byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));
                //返回base64字符串
                return Base64Utils.encodeToString(encrypted);
            } catch (Exception e) {
                throw new XkdException("AES_CBC加密异常:" + e.getMessage());
//                LOGGER.info("AES_CBC encrypt exception:" + e.getMessage());
//                return null;
            }

        } else {
            LOGGER.info("AES_CBC加密密钥长度不为16位:" + aesKey + "|" + iv_seed);
            throw new XkdException("密钥长度不为16位");
//            LOGGER.info("AES_CBC encrypt: the aesKey is null or error!");
//            return null;
        }
    }


    /**
     * AES/ECB/PKCS7Padding
     *
     * @param input
     * @param key
     * @return
     * @throws Exception
     * @throws NoSuchAlgorithmException
     */
    public static String encryptAESECB7(String input, String key) {
        try {
            SecretKeySpec skey = new SecretKeySpec(key.getBytes(), AES_ALGORITHM);
            Cipher cipher = Cipher.getInstance(AES_ECB_PKCS_7_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, skey);
            byte[] crypted = cipher.doFinal(input.getBytes("UTF8"));
            return new String(Base64.encodeBase64(crypted));
        } catch (Exception e) {
            throw new XkdException("AES/ECB/PKCS7Padding加密异常:" + e.getMessage());
//            e.printStackTrace();
//            return "";
        }
    }

    /**
     * AES_CBC解密
     *
     * @param content 待解密内容
     * @param aesKey  密码
     * @param iv_seed 偏移量
     * @return
     */
    public static String decryptCBC(String content, String aesKey, String iv_seed) {
        if (StringUtils.isBlank(content)) {
            LOGGER.info("AES_CBC decrypt: the content is null!");
            return null;
        }
        //判断秘钥是否为16位
        if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {
            try {
                //对密码进行编码
                byte[] bytes = aesKey.getBytes(ENCODING);
                //设置解密算法，生成秘钥
                SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);
                //偏移
                IvParameterSpec iv = new IvParameterSpec(iv_seed.getBytes(ENCODING));
                // "算法/模式/补码方式"
                Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);
                //选择解密
                cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

                //先进行Base64解码
                byte[] decodeBase64 = Base64Utils.decodeFromString(content);

                //根据待解密内容进行解密
                byte[] decrypted = cipher.doFinal(decodeBase64);
                //将字节数组转成字符串
                return new String(decrypted, ENCODING);
            } catch (Exception e) {
                LOGGER.info("AES_CBC decrypt exception:" + e.getMessage());
                throw new RuntimeException(e);
            }

        } else {
            LOGGER.info("AES_CBC解密密钥长度不为16位:" + aesKey + "|" + iv_seed);
            throw new XkdException("密钥长度不为16位");
        }
    }

    //生成16位数或者32位数AES密钥
    //keySize=128 16位数
    //keySize=256 32位数
    public static String generateAESKey(int keySize) throws Exception {
        // 创建 KeyGenerator 实例
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        // 设置密钥长度
        keyGenerator.init(keySize);
        // 生成密钥
        SecretKey secretKey = keyGenerator.generateKey();
        // 获取密钥字节数
        byte[] keyBytes = secretKey.getEncoded();
        // 将密钥编码为 Base64 字符串
        return bytesToHex(keyBytes, keySize);
    }

    private static String bytesToHex(byte[] bytes, int keySize) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        // 截取前 16 字符
        if (keySize == 128) {
            return sb.toString().substring(0, 16);
        } else if (keySize == 256) {
            return sb.toString().substring(0, 32);
        }
        return null;
    }
}
